#ifndef __M_LOOPER_H__
#define __M_LOOPER_H__

#include "buffer.hpp"
#include <mutex>
#include <thread>
#include <condition_variable>
#include <functional>
#include <memory>
#include <iostream>
#include <atomic>

namespace MyLog
{
    using Functor = std::function<void(Buffer&)>;

    enum class AsyncType
    {
        ASYNC_SAFE,   //安全状态，表示缓冲区满了则阻塞，避免资源耗尽的风险
        ASYNC_UNSAFE  //非安全状态，无限扩容，常用于测试
    };

    class AsyncLooper
    {
    public:
        using ptr = std::shared_ptr<AsyncLooper>;

        AsyncLooper(const Functor& callBack, AsyncType looperType = AsyncType::ASYNC_SAFE) 
            :_callBack(callBack)
            ,_isStop(false)
            ,_thread(&AsyncLooper::ThreadWorking, this) //要注意，类的成员函数有this指针
            ,_looperType(looperType)
        {}
    
        ~AsyncLooper()
        {
            _isStop = true;
            _condCon.notify_all(); //唤醒所有线程，其实这里就一个工作线程
            _thread.join();
        }

        void push(const char* data, size_t len)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            //写空间不够，阻塞
            if(_looperType == AsyncType::ASYNC_SAFE)
                _condPro.wait(lock, [&]()->bool{return _proBuf.writeAbleSize() >= len;});
            //写入
            _proBuf.push(data, len);
            //唤醒消费者
            _condCon.notify_all();
        }

    private:
        void ThreadWorking()   //线程工作函数
        {
            while(1)
            {
                //程序刚启动的时候，消费缓冲区肯定是没有数据的
                //所以先判断生产缓冲区有无数据，有则交换，无则阻塞
                {
                    std::unique_lock<std::mutex> lock(_mutex);

                    //错的错的
                    //首先 _proBuf.empty()；判断条件写错了
                    //这样就导致一直循环，_proBuf有数据反而阻塞了
                    //其次，_isStop为true表示停止运行，也不应该阻塞，要对加上对_isStop的判断
                    // _condCon.wait(lock, [&]()->bool{return _proBuf.empty();});    原先代码
                    _condCon.wait(lock, [&]()->bool{return !_proBuf.empty() || _isStop;});

                    //当_isStop被设置并且生产缓冲区为空退出
                    //不能只有_isStop，这样可能导致退出时缓存区还有数据没有处理
                    if(_isStop && _proBuf.empty())
                        return;

                    //交换两个缓冲区
                    _conBuf.swap(_proBuf);
                    //唤醒生产者
                    if(_looperType == AsyncType::ASYNC_SAFE)
                        _condPro.notify_one();   
                }

                //下面是对消费缓冲区的处理，不涉及临界区域，解锁
                _callBack(_conBuf);
                //重置缓冲区
                _conBuf.reset();
            }
        }

    private:
        /*
            异步工作器只是说对任务进行处理
            但是具体怎么处理他也不知道
            就比如说数据落地方向，写到文件还是屏幕中
        */
        Functor _callBack;   
    private:
        std::atomic<bool> _isStop;      //工作器停止标志
                                        //停止标志也是临界资源，设置成原子的可以减少加锁
        Buffer _proBuf;    //生产缓冲区
        Buffer _conBuf;    //消费缓冲区
        std::mutex _mutex;
        std::condition_variable _condPro;
        std::condition_variable _condCon;  //生产缓冲区中没有数据，消费缓冲区消费完后进入休眠
        std::thread _thread; //异步工作器对应的工作线程
        AsyncType _looperType;
    };

    
}

#endif